package cn.tedu.charging.device.start;
import cn.tedu.charging.common.constant.RedisConstants;
import cn.tedu.charging.device.mapper.StationMapper;
import cn.tedu.charging.common.pojo.po.ChargingStationPO;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;

import java.util.List;
/**
 *当前类是设备服务 负责站场 缓存预热的方案
 *其包含的知识点有
 * 1. redis的数据结构 geo string相关api
 * 2. redisTempalte封装的pipeLine
 */
@Component
@Slf4j
public class RedisWarmUp implements ApplicationRunner {
    @Autowired
    private StationMapper stationMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Override
    public void run(ApplicationArguments args) throws Exception {
        //1.批量读取数据
        int currentPage=1;
        IPage<ChargingStationPO> page;
        do{
            page = stationMapper.selectPage(new Page<>(currentPage++, 1000), null);
            List<ChargingStationPO> records = page.getRecords();
            if (CollectionUtils.isNotEmpty(records)){
                //本次批量数据非空 使用pipeLine写入redis
                List<Object> list = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
                    for (ChargingStationPO record : records) {
                        //先写缓存
                        String stationKey = RedisConstants.STATION_STRING_PREFIX + record.getId();
                        connection.set(RedisSerializer.string().serialize(stationKey), RedisSerializer.json().serialize(record));
                        //再写geo
                        String geoKey = RedisConstants.STATIONS_GEO;
                        connection.geoAdd(RedisSerializer.string().serialize(geoKey),
                                new Point(record.getStationLng().doubleValue(), record.getStationLat().doubleValue()), RedisSerializer.json().serialize(record.getId()));
                    }
                    return null;
                });
                log.debug("批量执行结果:{}",list);
            }
        }while (page.getCurrent()<=page.getPages());
    }

    /*@Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("redis预热程序开始运行");
        //1.读取数据库 charging.charging_station全表数据 需要持久层框架
        List<ChargingStationPO> pos = stationMapper.selectList(null);//没有查询条件 查询所有
        //2.组织一下放到redis中 放2个类型数据 一个geo 一个 string 需要redis客户端
        //在pos非空的时候 运行一下 缓存预热功能 保证缓存预热数据 幂等
        if (pos!=null&&pos.size()>0){
            for (ChargingStationPO po : pos) {
                saveStationCache(po);
            }
        }
        log.info("redis预热程序完成运行");
    }

    private void saveStationCache(ChargingStationPO po) {
        //1.把po 存储geo集合
        saveStationGeo(po);
        //2.把po 存储string缓存对象
        saveStationString(po);
    }

    private void saveStationString(ChargingStationPO po) {
        log.info("开始存储string缓存数据,当前value:{}",po);
        ValueOperations<String, ChargingStationPO> opsForValue = redisTemplate.opsForValue();
        //key值 使用的是站场的id value就是po对象
        //redis中使用string做缓存 key值绑定业务 但是要添加业务前缀
        opsForValue.set(RedisConstants.STATION_STRING_PREFIX+po.getId(),po);
    }

    private void saveStationGeo(ChargingStationPO po) {
        //多次调用 多次启动 是否会出现冗余的数据
        log.info("开始存储geo元素,当前元素值:{}",po.getId());
        //明确数据类型对应关系 String就是geo的集合key是字符串 Integer geo元素数据类型 Integer id
        GeoOperations<String,Integer> opsForGeo = redisTemplate.opsForGeo();
        Point point = new Point(po.getStationLng().doubleValue(), po.getStationLat().doubleValue());
        opsForGeo.add(RedisConstants.STATIONS_GEO,point,po.getId());
    }*/
}
